package com.geor.gcv.ict;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.nutz.json.Json;
import org.nutz.lang.Files;
import org.nutz.lang.Lang;
import org.opencv.core.Core;
import org.opencv.core.Mat;
import org.opencv.core.Size;
import org.opencv.imgcodecs.Imgcodecs;
import org.slf4j.Logger;

import com.geor.common.log.Loggers;
import com.geor.common.msg.Strings;
import com.geor.gcv.core.mat.Mats;
import com.geor.gcv.core.model.Area;
import com.geor.gcv.core.test.TestSwitch;
import com.geor.gcv.core.util.DataTool;
import com.geor.gcv.ict.data.ClassifierResult;
import com.geor.gcv.ict.util.PropertiesReader;
import com.geor.gcv.launcher.TfException;

/**
 * ccs（python汉字识别服务）请求客户端
 * 
 * @author geor.lusongnan, geor.yuli
 *
 */
@Deprecated
public class CCSClient {
	static Logger log = Loggers.get();

	// static String CHARSET = "UTF-8";
	static int TIMEOUT = 60 * 1000;
	static String ADDRESS = "";
	static int PORT = 9603;
	static Map<String, String> dictionary;

	CCSSender sender;

	static {
		ADDRESS = PropertiesReader.get("server.listener.address");
		PORT = Integer.parseInt(PropertiesReader.get("server.ccs.port"));
		File dictionaryFile = new File(PropertiesReader.get("command.dictionary"));
		log.debug(String.format("CCS client init param: {address: %s, port: %d, dictpath: %s}", ADDRESS, PORT,
				dictionaryFile.getAbsolutePath()));
		dictionary = (Map<String, String>) Json.fromJsonAsMap(String.class, Files.read(dictionaryFile));
	}

	public CCSClient() {
	}

	public CCSClient(String ip, int p) {
		CCSClient.ADDRESS = ip;
		CCSClient.PORT = p;
	}

	// public String request(List<Mat> mats) {
	// List<String> filepaths = new ArrayList<String>();
	// String fid = String.valueOf(System.nanoTime());
	// File shardFolder = new File(System.getProperty("user.dir") +
	// "/tmp/shard", fid);
	// try {
	// if (!shardFolder.exists()) {
	// shardFolder.mkdirs();
	// }
	// for (int i = 0; i < mats.size(); i++) {
	// String shardpath = shardFolder.getAbsolutePath() + File.separator +
	// String.format("%05d.png", i);
	// Highgui.imwrite(shardpath, mats.get(i));
	// filepaths.add(shardpath);
	// }
	// return requestByPaths(shardFolder.getAbsolutePath());
	// } finally {
	// Files.deleteDir(shardFolder);
	// }
	// }

	public List<ClassifierResult> request(List<Mat> mats) {
		for (int i = 0; i < mats.size(); i++) {
			Mat shard = mats.get(i);
			mats.set(i, Mats.resize(shard, new Size(64, 64)));
		}

		Mat result = new Mat();
		Core.hconcat(mats, result);

		String fid = DataTool.getUUID();
		File shardFolder = new File(System.getProperty("user.dir") + "/tmp/shard", fid);
		try {
			if (!shardFolder.exists()) {
				shardFolder.mkdirs();
			}
			String shardpath = shardFolder.getAbsolutePath() + File.separator + "shard.png";
			Imgcodecs.imwrite(shardpath, result);
			return requestByPaths(shardFolder.getAbsolutePath());
		} finally {
			Files.deleteDir(shardFolder);
		}
	}

	private List<ClassifierResult> _request(String dirpath) throws TfException, Exception {
		try {
			sender = new CCSSender(ADDRESS, PORT);
			log.debug("ready to send:" + dirpath);
			String response = sender.send(dirpath);
			log.debug("received:" + response);

			response = response.replace(", dtype=int32", "");
			List<ClassifierResult> ccsResults = parsing(response);

			if (TestSwitch.isDebug()) {
				File file = new File(dirpath, "shard.png");
				Mat shard = Mats.imread(file.getAbsolutePath());
				int size = shard.cols() / 64;
				for (int i = 0; i < size; i++) {
					Area area = new Area(i * 64, 0, 64, 64);
					Mat m = Mats.screenShoot(shard, area);
					File imgdir = new File("tf", ccsResults.get(i).responses[0]);
					if (!imgdir.exists()) {
						imgdir.mkdirs();
					}
					Mats.imwrite(new File(imgdir, DataTool.getUUID() + ".png").getAbsolutePath(), m);
				}
			}
			return ccsResults;
		} catch (TfException e) {
			throw e;
		} catch (Exception e1) {
			throw e1;
		}
	}

	// private void parsing(List<CCSResult> ccsResults, String response2) {
	// String strs[] = response2.split("]]\\)");
	// for (int n = 0 ; n < strs.length ; n ++) {
	// String ss[] = strs[n].split("\\(\\[\\[");
	// if(ss.length == 2) {
	// String s[] = ss[1].split(",");
	// ccsResults.get(n).similarites = new float[s.length];
	// for (int i = 0; i < s.length; i++) {
	// String key = s[i];
	// ccsResults.get(n).similarites[i] = Float.parseFloat(key);
	// }
	// }
	// }
	// }

	private List<ClassifierResult> parsing(String response1) throws TfException {
		List<ClassifierResult> ccsResults = new ArrayList<ClassifierResult>();
		String strs[] = response1.split("]]\\)");
		for (String string : strs) {
			ClassifierResult result = new ClassifierResult();
			String ss[] = string.split("\\(\\[\\[");
			if (ss.length == 2) {
				String s[] = ss[1].split(",");
				String rs[] = new String[s.length];
				for (int i = 0; i < s.length; i++) {
					String key = s[i];
					String character = dictionary.get(key.trim());
					rs[i] = character;
					result.responses = rs;
				}
				ccsResults.add(result);
			}
		}

		if (Lang.isEmpty(ccsResults) || "?".equals(ccsResults.get(0).getResponse())) {
			throw new TfException();
		}
		return ccsResults;
	}

	/**
	 * TF识别碎片
	 * 
	 * @param filepaths
	 * @return
	 */
	private List<ClassifierResult> requestByPaths(String dirpath) {
		if (Lang.isEmpty(dirpath)) {
			return new ArrayList<ClassifierResult>();
		}
		try {
			List<ClassifierResult> results = _request(dirpath);
			return results;
		} catch (TfException te) {
			// 有响应时抛出的异常，重新识别
			try {
				Thread.sleep(1000);
				return _request(dirpath);
			} catch (Exception e) {
				log.error("cf classifier retry failed");
				return new ArrayList<ClassifierResult>();
			}
		} catch (Exception e) {
			log.error("cf classifier exception");
			return new ArrayList<ClassifierResult>();
		}
	}

	private class CCSSender {
		Socket socket;
		String adress;
		int port;

		public CCSSender(String adress, int port) throws IOException {
			super();
			this.adress = adress;
			this.port = port;
		}

		public String send(String request) throws IOException {
			socket = new Socket(adress, port);
			socket.setSoTimeout(TIMEOUT);
			if (Lang.isEmpty(request)) {
				throw new IllegalArgumentException("result must be null !");
			}
			OutputStream os = null;
			InputStream is = null;
			try {
				os = socket.getOutputStream();
				BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(os));

				is = socket.getInputStream();
				BufferedReader reader = new BufferedReader(new InputStreamReader(is));

				writer.write(request);
				writer.flush();

				StringBuffer buffer = new StringBuffer();
				String response = null;
				while ((response = reader.readLine()) != null) {
					buffer.append(response.trim());
				}
				return buffer.toString();
			} finally {
				if (null != os)
					os.close();
				if (null != is)
					is.close();
				if (null != socket)
					socket.close();
			}
		}

		@Override
		public String toString() {
			return Strings.format("chinese request adress:[%s], port: [%d].", adress, port);
		}

	}

}
